//    Copyright (C) Kherty.  All rights reserved.
using System;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.IO;

namespace OpenLS.Spreadsheet.Formats.Biff.Records
{
    sealed class FormulaValue
    {
        public override string ToString()
        {
            return "{" + _code + "}";
        }

        public FormulaValue()
        {

        }

        public FormulaValue(object value)
        {
            Contract.Requires(value != null);
            this.Value = value;
        }

        private void SetCodeFromValue(object value)
        {
            if (value is double)
            {
                _code = ConvertFromDouble((double)value);
                return;
            }
            if (value is string)
            {
                string s = (string)value;
                if (s.Length > 0)
                {
                   //\\ this._code = ConvertFromDouble(double.NaN);
                    _code = 0xffff000000000000;
                    return;
                }
                _code = 0xffff000000000003;
                return;
            }
            if (value is bool)
            {
                bool b = (bool) value;
                if (b)
                    _code = 0xffff000f3f010001;
                else
                {
                    _code = 0xffff000f3f000001;
                }
            }
            if (value is Error)
            {
                Error error = (Error) value;
                _code = 0xffff000000000002;
                _code |= (ulong) error.Type << 16;
#if DEBUG
                Error error2 = (Error) GetValueFromCode();
                Contract.Assert(error.Type == error2.Type);
#endif
                   
            }
        }

        private double ConvertToDouble(ulong @ulong)
        {
            byte[] data = new byte[08];
            using (var s = new MemoryStream(data))
            {
                using (BinaryWriter w = new BinaryWriter(s))
                {
                    w.Write(@ulong);
                }
            }
            using (var s = new MemoryStream(data))
            {

                using (BinaryReader reader = new BinaryReader(s))
                {
                    return reader.ReadDouble();
                }
            }
        }
        private ulong ConvertFromDouble(double @ulong)
        {
            byte[] data = new byte[08];
            using (var s = new MemoryStream(data))
            {
                using (BinaryWriter w = new BinaryWriter(s))
                {
                    w.Write(@ulong);
                }
            }
            using (var s = new MemoryStream(data))
            {
              //\\  Debug.WriteLine(string.Join(",", data));
                using (BinaryReader reader = new BinaryReader(s))
                {
                    return reader.ReadUInt64();
                }
            }
        }

        private object _valueCache;
        internal object Value
        {
            get
            {
                Contract.Ensures(! (Contract.Result<object>() is IXLString));
                return _valueCache;
            }
            set
            {
                Contract.Requires(!(value is IXLString));
                Contract.Ensures(GetValueFromCode() == value || GetValueFromCode().Equals(value));
                SetCodeFromValue(value);


                _valueCache = value;
            }
        }

        ulong _code;


        internal object GetValueFromCode()
        {
            if (Value is string)
                return Value;
            ulong l = _code >> 8 * 6;
            if (l == 0xFFFF)
            {
                switch (_code & 0xFF)
                {
                    case 1:
                        ulong i = _code & 0X0000000000FF0000;
                        if (i == 0)
                            return false;
                        return true;
                    case 2:
                        ulong ii = _code & 0X0000000000FF0000;
                        ii = ii >> (8 * 2);
                        return Errors.GetError((ErrorType)ii);
                    case 3:
                        return "";
                }
            }
            var result =  ConvertToDouble(_code);
            return result;

        }

        public void Write(BinaryWriter writer)
        {
            writer.Write(_code);
        }

        public void Read(BinaryReader reader)
        {
            this._code = reader.ReadUInt64();
        }
    }
}